#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#define MAX_TOKEN_LEN 100

typedef struct {
    char type[MAX_TOKEN_LEN];   
    char value[MAX_TOKEN_LEN];  
    int line;
    int column;
} Token;

// 仓颉关键字
const char *keywords[] = {
    "as", "break", "case", "const", "continue",
    "do", "else", "for", "from", "func",
    "if", "in", "int", "let", "main",
    "match", "read","var", "while","write",
    NULL
};

int line = 1;
int column = 1;
FILE *source_file;

Token get_next_token();
int is_keyword(const char *str);
void print_token(Token token);

void init_lexer(const char *filename) {
    source_file = fopen(filename, "r");
    if (!source_file) {
        printf("Error: Cannot open file %s\n", filename);
        exit(1);
    }
    line = 1;
    column = 1;
}


Token get_next_token() {
    Token token;
    int ch;

    memset(token.type, 0, MAX_TOKEN_LEN);
    memset(token.value, 0, MAX_TOKEN_LEN);

    // 1. 跳过空白字符
    while ((ch = fgetc(source_file)) != EOF) {
        if (ch == '\n') {
            line++;
            column = 1;
            continue;
        } else if (isspace(ch)) {
            column++;
            continue;
        } else {
            ungetc(ch, source_file);
            break;
        }
    }

    token.line = line;
    token.column = column;

    ch = fgetc(source_file);
    if (ch == EOF) {
        strcpy(token.type, "EOF");
        strcpy(token.value, "EOF");
        return token;
    }

    // 2. 标识符 / 关键字
    if (isalpha(ch) || ch == '_') {
        int i = 0;
        token.value[i++] = ch;
        column++;
        while ((ch = fgetc(source_file)) != EOF &&
               (isalnum(ch) || ch == '_')) {
            if (i < MAX_TOKEN_LEN - 1) {
                token.value[i++] = ch;
            }
            column++;
        }
        token.value[i] = '\0';
        if (ch != EOF) {
            ungetc(ch, source_file);
        }
        if (is_keyword(token.value)) {
            strcpy(token.type, token.value);
        } else {
            strcpy(token.type, "IDENTIFIER");
        }
        return token;
    }

    // 3. 数字
    if (isdigit(ch)) {
        int i = 0;
        token.value[i++] = ch;
        column++;

        while ((ch = fgetc(source_file)) != EOF && isdigit(ch)) {
            if (i < MAX_TOKEN_LEN - 1) {
                token.value[i++] = ch;
            }
            column++;
        }
        token.value[i] = '\0';
        if (ch != EOF) {
            ungetc(ch, source_file);
        }
        strcpy(token.type, "NUMBER");
        return token;
    }

    // 4. 运算符：+ - * / = < > ! & | .
    if (strchr("+-*/=<>!&|.", ch)) {
        int i = 0;
        token.value[i++] = ch;
        column++;

        int next_ch = fgetc(source_file);
        if (next_ch != EOF) {
            char double_op[3] = { (char)ch, (char)next_ch, '\0' };
            if (strstr("== != <= >= && || ++ -- ..", double_op)) {
                token.value[i++] = next_ch;
                column++;
            } else {
                ungetc(next_ch, source_file);
            }
        }
        token.value[i] = '\0';

        strcpy(token.type, token.value);
        return token;
    }

    // 5. 分隔符：;,(){}[]:
    if (strchr(";,(){}[]:", ch)) {
        token.value[0] = ch;
        token.value[1] = '\0';
        column++;
        strcpy(token.type, token.value);
        return token;
    }

    // 6. 字符串
    if (ch == '"') {
        int i = 0;
        token.value[i++] = ch;
        column++;

        while ((ch = fgetc(source_file)) != EOF && ch != '"') {
            if (i < MAX_TOKEN_LEN - 1) {
                token.value[i++] = ch;
            }
            column++;
            if (ch == '\n') {
                line++;
                column = 1;
            }
        }

        if (ch == '"') {
            if (i < MAX_TOKEN_LEN - 1) {
                token.value[i++] = ch;
            }
            column++;
        }
        token.value[i] = '\0';
        strcpy(token.type, "STRING");
        return token;
    }

    // 7. 未知字符
    token.value[0] = ch;
    token.value[1] = '\0';
    column++;
    strcpy(token.type, "UNKNOWN");
    return token;
}

int is_keyword(const char *str) {
    for (int i = 0; keywords[i] != NULL; i++) {
        if (strcmp(keywords[i], str) == 0) {
            return 1;
        }
    }
    return 0;
}

void print_token(Token token) {
    printf("%d\t%d\t%-12s\t%s\n",
           token.line, token.column,
           token.type, token.value);
}
